package com.hdsx.spring.webservlet.core;

import com.alibaba.fastjson2.JSON;
import com.hdsx.spring.tomcat.request.HttpServletRequest;
import com.hdsx.spring.tomcat.response.HttpServletResponse;
import com.hdsx.spring.tomcat.servlet.HttpServlet;
import com.hdsx.spring.webservlet.annotation.Autowired;
import com.hdsx.spring.webservlet.annotation.Controller;
import com.hdsx.spring.webservlet.annotation.RequestMapping;
import com.hdsx.spring.webservlet.annotation.Service;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * @author 占贤银
 * time 2024/8/16 15:07
 * description 生命之灯因热情而点燃，生命之舟因拼搏而前行
 */
public class DispatcherServlet extends HttpServlet {
    private Properties prop = new Properties();

    private List<String> classNames = new ArrayList<>();

    private Map<String, Method> handlerMappings = new HashMap<>(10);

    private Map<String, Object> registerMaps = new HashMap<>(10);

    @Override
    public void init() {
        //todo 1、加载配置文件
        String packageName = doLocationConfig();
        // todo 2、扫描指定的类
        doScan(packageName);
        // todo 3、将扫描到的类注册到IOC容器中
        doRegister();

        // todo 4、实现依赖注入
        doAutowired();

        // todo 5、实现url - method之间的映射关键
        doHandlerMappings();

        System.out.println("到此为止，简约版本的spring框架完成了！");
    }

    private void doHandlerMappings() {
        try {
            for (Map.Entry<String, Object> entry : registerMaps.entrySet()) {
                Object instance = entry.getValue();
                Class<?> aClass = instance.getClass();
                //StringBuilder baseUrl = new StringBuilder("/////");
                String baseUrl = "";
                if (aClass.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping requestMapping = aClass.getAnnotation(RequestMapping.class);
                    baseUrl = requestMapping.value().trim();
                }
                Method[] methods = aClass.getDeclaredMethods();
                for (Method method : methods) {
                    if (!method.isAnnotationPresent(RequestMapping.class)) {
                        continue;
                    }
                    RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                    if ("".equalsIgnoreCase(requestMapping.value().trim())) {
                        throw new RuntimeException(method.getName() + "方法上的RequestMapping注解中的value属性不能为空！");
                    }
                    String url = ("/" + baseUrl + "/" + requestMapping.value().trim()).replaceAll("/+", "/");
                    if (handlerMappings.containsKey(url)) {
                        throw new RuntimeException("Ambiguous mapping. Cannot map '"+lowerToCaseStr(method.getDeclaringClass().getSimpleName())+"' method，请您认真检查一下！");
                    }
                    handlerMappings.put(url, method);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Object getBean(String beanName) {
        return registerMaps.get(beanName);
    }
    private <T> T getBean(Class<T> beanClass) {
        String beanName = lowerToCaseStr(beanClass.getSimpleName());
        if (beanClass.isInterface()) {
            beanName = beanClass.getName();
        }
        return (T) getBean(beanName);
    }

    /**
     * 实现依赖注入
     */
    private void doAutowired() {
        try {
            for (Map.Entry<String, Object> entry : registerMaps.entrySet()) {
                Object instance = entry.getValue();
                Class aClass = instance.getClass();
                Field[] fields = aClass.getDeclaredFields();
                for (Field field : fields) {
                    if (!field.isAnnotationPresent(Autowired.class)) {
                        continue;
                    }
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    Class<?> fieldType = field.getType();
                    Object fieldInstance = getBean(fieldType);
                    if (fieldInstance == null) {
                        fieldInstance = getBean(autowired.value());
                    }
                    if (fieldInstance == null) {
                        throw new RuntimeException("在springIoc上下文中，没有找到指定类型【" + fieldType + "】的bean，请先加入到Ioc容器中！");
                    }
                    // todo 强吻
                    field.setAccessible(true);
                    field.set(instance, fieldInstance);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doRegister() {
        try {
            for (String className : classNames) {
                Class<?> aClazz = Class.forName(className);
                if (aClazz.isInterface()) {
                    continue;
                }
                if (aClazz.isAnnotationPresent(Controller.class)) {
                    Controller controller = aClazz.getAnnotation(Controller.class);
                    String beanName = lowerToCaseStr(aClazz.getSimpleName());
                    if (!"".equals(controller.value())) {
                        beanName = controller.value();
                    }
                    registerMaps.put(beanName, aClazz.newInstance());
                } else if (aClazz.isAnnotationPresent(Service.class)) {
                    Service service = aClazz.getAnnotation(Service.class);
                    String beanName = lowerToCaseStr(aClazz.getSimpleName());
                    if (!"".equals(service.value())) {
                        beanName = service.value();
                    }
                    registerMaps.put(beanName, aClazz.newInstance());
                    for (Class<?> anInterface : aClazz.getInterfaces()) {
                        registerMaps.put(anInterface.getName(), aClazz.newInstance());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }

    private String lowerToCaseStr(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return new String(chars);
    }

    public void doScan(String packageName) {
        try {
            URL url = this.getClass().getClassLoader()
                    .getResource(packageName.replaceAll("\\.", "/"));
            File file = new File(url.getFile());
            for (File listFile : file.listFiles()) {
                String name = listFile.getName();
                if (listFile.isDirectory()) {
                    doScan(packageName + "." + name);
                } else if (listFile.isFile()) {
                    if (!name.endsWith(".class")) {
                        continue;
                    }
                    classNames.add((packageName + "." + name).replaceAll(".class", ""));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }

    private String doLocationConfig() {
        InputStream in = null;
        try {
            in = this.getClass().getClassLoader().getResourceAsStream("spring.properties");
            prop.load(in);
            return prop.getProperty("compoentScan");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
    // http://localhost:8080/index/login

    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) {
        try {
            if (!handlerMappings.containsKey(request.getUrl().trim())) {
                response.write("<h1 style='color:blue;'>大家好，使用Netty作为基础服务架构，手写spring初体验！<h1>");
                return;
            }
            Method method = handlerMappings.get(request.getUrl().trim());
            String beanName = lowerToCaseStr(method.getDeclaringClass().getSimpleName());
            if (method.getDeclaringClass().isInterface()) {
                beanName = method.getDeclaringClass().getName();
            }
            Object result = method.invoke(getBean(beanName));
            if (result instanceof String) {
                response.write(result.toString());
            } else if (result instanceof Object) {
                response.write(JSON.toJSONString(result));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //
    }
}